package feizhou.untils.tools.gis;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;



/*create by yangdawei 2016-12-14*/
/*抽稀原理：
1、对曲线的首末点虚连一条直线，求曲线上所有点与直线的距离，并找出最大距离值dmax，用dmax与事先给定的阈值D相比： 
2、若dmax<dist，则将这条曲线上的中间点全部舍去；则该直线段作为曲线的近似，该段曲线处理完毕。 
　 若dmax<=dist，保留dmax对应的坐标点，并以该点为界，把曲线分为两部分，对这两部分重复使用该方法，即重复1、2步，直到所有dmax均<dist，即完成对曲线的抽稀。 
*/

public class Smooth
 {
     private List<GPoint> gpslist;//传进来的gps坐标数组
     private List<GPoint> maclist;//转成墨卡托的坐标数组
     private List<Integer> indexlist = new ArrayList<Integer>();//存储保留点的索引
     private double threshold;//平滑系数阈值

     /// <summary>
     /// 构造函数
     /// </summary>
     /// <param name="inlist">传进来的gps坐标数组</param>
     /// <param name="dvalue">平滑系数</param>
     public Smooth(List<GPoint> inlist, double dvalue)
     {
         threshold = dvalue;
         gpslist = inlist;
         if (inlist != null && inlist.size() > 2 && dvalue > 0)
         {     
             maclist = GPointListToMecator(inlist);
             indexlist.add(0);
             GetMaxDistId(0, maclist.size() - 1);
             indexlist.add(maclist.size() - 1);
         }

     }

     ///调用此方法返回抽稀后的线段
     public List<GPoint> Result()
     {
         if (gpslist != null && gpslist.size() > 2 && threshold > 0)
         {
            // indexlist.Sort();
             Collections.sort(indexlist);//重新排序
             List<GPoint> gplist = new ArrayList<GPoint>();
             for (int i = 0; i < indexlist.size(); i++)
             {
                 gplist.add(gpslist.get(indexlist.get(i)));

             }


             return gplist;
         }
         else
         {
             return gpslist;

         }

     }
     
     
     private void GetMaxDistId(int start, int end)
     {
         int maxindex = 0;       
         if (end - start > 1)//起码间隔一个点
         {
             Slope lineslp = Coefficient(maclist.get(start), maclist.get(end));//求起点与终点连线的斜率参数
             double dmax = 0;
             for (int i = start + 1; i < end; i++)
             {
                 double dist = GetDistance(maclist.get(i), lineslp);
                 if (dist > threshold && dist > dmax)
                 {
                     dmax = dist;
                     maxindex = maclist.get(i).index;

                 }

             }

         }
         if (maxindex != 0)
         {
             indexlist.add(maxindex);
             if (maxindex - start > 1)
             {
                 GetMaxDistId(start, maxindex);
             }
             if (end - maxindex > 1)
             {
                 GetMaxDistId(maxindex,end);
             }
         }
     }







     private List<GPoint> GPointListToMecator(List<GPoint> listin)
     {
         ArrayList<GPoint> gplist = new ArrayList<GPoint>();
         for (int i = 0; i < listin.size(); i++)
         {
             GPoint pin = listin.get(i);
             GPoint pout = lonLat2Mercator(pin);
             gplist.add(pout);

         }

         return gplist;
     }




     /// <summary>
     /// 两点求y=kx+b直线参数
     /// 之所以不直接把点与线的距离放在这里合起来求是因为直线可能会多次调用，避免多次运算耗时
     /// </summary>
     /// <param name="p1">点1</param>
     /// <param name="p2">点2</param>
     /// <returns>直线的k与b</returns>
     private Slope Coefficient(GPoint p1, GPoint p2)
     {
         Slope xl = new Slope();
         if (p2.x == p1.x)
         {
             xl.isxx = true;
             xl.xx = p2.x;
         }
         else
         {
             xl.isxx = false;   
             double k = (p2.y - p1.y) / (p2.x - p1.x);
             xl.k = k;
             xl.b = p1.y - k * p1.x;
        
         }
         return xl;
     }

     /// <summary>
     /// 点与直线y=kx+b的距离
     /// </summary>
     /// <param name="pt">点</param>
     /// <param name="cs">y=kx+b</param>
     /// <returns>距离</returns>
     private double GetDistance(GPoint pt, Slope cs)
     {
         double dis = 0;
         if (cs.isxx == false)
         {
             dis = (Math.abs(cs.k * pt.x - pt.y + cs.b)) / Math.sqrt(cs.k * cs.k + 1);
         }
         else {
             dis = Math.abs(pt.x-cs.xx);

         }
         return dis;
     }



     /// <summary>
     /// 经纬度转墨卡托
     /// </summary>
     /// <param name="pin"></param>
     /// <returns></returns>
     private GPoint lonLat2Mercator(GPoint pin)
     {
         GPoint pout = new GPoint();
         double x = pin.x * 20037508.3427892 / 180;
         double y = Math.log(Math.tan((90 + pin.y) * Math.PI / 360)) / (Math.PI / 180);
         y = y * 20037508.3427892 / 180;
         pout.x = x;
         pout.y = y;
         pout.z = pin.z;
         pout.index = pin.index;
         pout.speed = pin.speed;
         pout.time = pin.time;
         return pout;
     }
     /// <summary>
     /// 墨卡托转经纬度
     /// </summary>
     /// <param name="pin"></param>
     /// <returns></returns>
     private GPoint Mercator2lonLat(GPoint pin)
     {
         GPoint pout = new GPoint();
         double x = pin.x / 20037508.3427892 * 180;
         double y = pin.y / 20037508.3427892 * 180;
         y = 180 / Math.PI * (2 * Math.atan(Math.exp(y * Math.PI / 180)) - Math.PI / 2);
         pout.x = x;
         pout.y = y;
         pout.index= pin.index;
         pout.z = pin.z;
         pout.speed = pin.speed;
         pout.time = pin.time;
         return pout;
     }





 }

   /*以下是两个用到的类*/


    /// <summary>
    /// 直线y=kx+b的k与b
    /// </summary>





